﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using NabNetLib;
using NabNetLib.Clapier;

namespace NabNetWeb
{
    public partial class _bc : System.Web.UI.Page
    {

        private static object mLocker = new object();

        protected void Page_Load(object sender, EventArgs e)
        {
            string bootCodePath = ConfigurationManager.AppSettings[Constants.BOOT_CODE_PATH_KEY];

            if(!Path.IsPathRooted(bootCodePath))
            {
                bootCodePath = Context.Server.MapPath(bootCodePath);
            }

            string serialNumber = Request.QueryString["m"];
            if(!string.IsNullOrEmpty(serialNumber))
            {
                serialNumber = serialNumber.Replace(":", "");
            }

            if (!string.IsNullOrEmpty(serialNumber))
            {
                using (nabnetEntities objectContext = new nabnetEntities())
                {

                    Lapin lapin = LapinManager.GetLapin(objectContext, serialNumber);

                    if (lapin == null)
                    {
                        //unknown lapin --> register it ?
                        bool allowAnonymousConnection = false;
                        bool.TryParse(ConfigurationManager.AppSettings[Constants.ALLOW_ANONYMOUS_CONNECTION_KEY],
                                      out allowAnonymousConnection);
                        if (allowAnonymousConnection)
                        {
                            lapin = LapinManager.RegisterNewLapin(objectContext, serialNumber, serialNumber,
                                                                  string.Empty);
                        }
                    }

                    if (lapin != null
                        && string.IsNullOrEmpty(lapin.password)
                        && ConfigurationManager.AppSettings[Constants.AUTH_MODE_KEY] == "patched")
                    {
                        string patchedBootCodePath = bootCodePath + "_patched";
                        if (!File.Exists(patchedBootCodePath))
                        {

                            lock (mLocker)
                            {
                                byte[] buffer = null;

                                using (FileStream stream = new FileStream(bootCodePath, FileMode.Open))
                                {
                                    buffer = new byte[stream.Length];
                                    stream.Read(buffer, 0, (int) stream.Length);
                                }
                                using (MemoryStream patchedStream = new MemoryStream(buffer))
                                {
                                    PatchBootCode(patchedStream);

                                    patchedStream.Seek(0, SeekOrigin.Begin);

                                    using (FileStream stream = new FileStream(patchedBootCodePath, FileMode.Create))
                                    {
                                        byte[] bufferPatched = new byte[patchedStream.Length];
                                        patchedStream.Read(bufferPatched, 0, (int) patchedStream.Length);
                                        stream.Write(buffer, 0, bufferPatched.Length);
                                    }
                                }
                            }
                        }
                        bootCodePath = patchedBootCodePath;
                    }
                }
            }

            Response.WriteFile(bootCodePath);
        }


        private void PatchBootCode(MemoryStream stream)
        {
            
			//LogDebug("Analyzing firmware for patch application");

			long address1 = 0x00011A4A;
			long address2 = 0x00011A91;
			long address3 = 0x00011AC9;
			long address4 = 0x00011AEB;

            byte[] origin1 = new byte[] { 0x02, 0x3D, 0x00 };
            byte[] origin2 = new byte[] { 0x02, 0x3D, 0x00, 0x02, 0x3E, 0x00 };
            byte[] origin3 = new byte[] { 0x02, 0x3D, 0x00, 0x02, 0x3E, 0x00 };
            byte[] origin4 = new byte[] { 0x02 };

            byte[] patch1 = new byte[] { 0x07,0x00,0x05 };
            byte[] patch2 = new byte[] { 0x04, 0x05, 0x04, 0x05, 0x04, 0x05 };
            byte[] patch3 = new byte[] { 0x04, 0x05, 0x04, 0x05, 0x04, 0x05 };
            byte[] patch4 = new byte[] { 0x01 };

			bool patch = true;

            patch &= CheckPatch(stream, address1, origin1);
            patch &= CheckPatch(stream, address2, origin2);
            patch &= CheckPatch(stream, address3, origin3);
            patch &= CheckPatch(stream, address4, origin4); 
            
            
            if(patch)
			{
                //LogDebug("Patching firmware");

                stream.Seek(address1, SeekOrigin.Begin);
                stream.Write(patch1,0,patch1.Length);

                stream.Seek(address2, SeekOrigin.Begin);
                stream.Write(patch2, 0, patch2.Length);

                stream.Seek(address3, SeekOrigin.Begin);
                stream.Write(patch3, 0, patch3.Length);

                stream.Seek(address4, SeekOrigin.Begin);
                stream.Write(patch4, 0, patch4.Length);
			}
        }

        private bool CheckPatch(MemoryStream stream, long address, byte[] expectedBytes)
        {
            byte[] buffer = new byte[expectedBytes.Length];
            stream.Seek(address, SeekOrigin.Begin);
            stream.Read(buffer, 0, buffer.Length);

            bool test = true;
            for(int i=0;i<buffer.Length && test;i++)
            {
                test &= (buffer[i] == expectedBytes[i]);
            }
            return test;
        }
    }
}
